import Native from "./native.js"
import promise from "../utils/wx_promise.js"
const $log = require('../utils/log.js')
import { arrayToHexString, decodeWeight, buildCmd, isHeartRateScale, sleep } from "./utils.js"
const moment = require('../libs/moment')

const native = new Native()

import {
  getUUID,
  UUID_WSP_181D,
  UUID_WSP_1805,
  UUID_WSP_READ,
  UUID_WSP_WRITE,
  UUID_WSP_FFE1,
  UUID_WSP_FFE2,
  UUID_WSP_2A2B,
  UUID_WSP_2A9E,
  UUID_WSP_2A9D,
  UUID_WSP_181C,
  UUID_WSP_2A9F,
  UUID_WSP_181B,
  UUID_WSP_2A9B,
  UUID_WSP_2A8C,
  UUID_WSP_2A8E,
  UUID_WSP_2A85,
  UUID_WSP_2A80,
  UUID_WSP_2AFF,
  UUID_WSP_2A9C,
  wspTime,
  algorithmValue,
  isWsp,
} from '../utils/wsp.js'

function logTime() {
    let _moment = moment()

    return `${_moment.format("YYYY-MM-DD H:mm:ss")} (${_moment.valueOf()})`
}

class Main {

  device = null
  available = false
  discovering = false
  readCharacteristicId = null
  onDeviceScanObj = {}
  macObj = {}
  connectStateChangeList = []
  connecting = false
  connected = false
  oneToOne = false

  wspMode = false

  constructor(obj = {}) {
    this.listener = obj
    this.readCharacteristicId = this.listener.readCharacteristicId

    if (this.listener.services && this.listener.services.length) {
      native.servicesList.push(...this.listener.services)
    }

    //native.deviceFoundListPush(this.onDeviceScan.bind(this))
    native.deviceFoundList.scale = this.onDeviceScan.bind(this)

    native.onAdapterStateChange = this.onAdapterStateChange.bind(this)

    //native.connectStateChangeListPush(this.onConnectStateChange.bind(this))
    native.connectStateChangeList.scale = this.onConnectStateChange.bind(this)

    if (native.status) {
      return
    } else {
      native.status = true
      native.onGlobalEvent()
    }

  }

  onDeviceScanObjPush(type, fn) {
    this.onDeviceScanObj[type] = fn
  }

  macObjPush(type, fn) {
    this.macObj[type] = fn
  }

  onValueChangePush(cb) {
    native.onValueChangeList.sclae = cb
          /* native.onValueChangePush(cb) */
  }

  startScan() {
    this.device = null
    this.oneToOne = false
    this.wspMode = false

    return sleep(60).then(() => {
      return this.openAdapter()
    }).then(() => {
      return sleep(30)
    }).then(() => {
      return this.startDiscovery()
    }).catch(err => {
      this.onError(err)
    })
  }

  restart() {
    return this.closeConnect().then(() => {
      return this.stopDiscovery()
    }).then(() => {
      this.device = null
      this.oneToOne = false
      this.wspMode = false
      return sleep(300)
    }).then(() => {
      return this.startDiscovery()
    })
  }

  openAdapter() {
      /* if (this.available) {
        return this.closeAdapter().then(() => {
          return sleep(30)
        }).then(() => {
          console.log("打开蓝牙模块")
          return native.openAdapter()
        })
      } */
    console.log("打开蓝牙模块")
    return native.openAdapter()
  }

  closeAdapter() {
    console.log("关闭蓝牙模块")
    return native.closeAdapter().catch(err => {
      console.error("蓝牙适配器关闭失败", err)
      this.onError(err)
    })
  }

  startDiscovery() {
    console.log("开始搜寻附近的蓝牙外围设备")
    return native.startDiscovery(native.servicesList).then(() => {
      this.discovering = true
      return {}
    })
  }

  stopDiscovery() {
    if (this.discovering) {
      console.log("停止搜寻附近的蓝牙外围设备")
      return native.stopDiscovery().then(() => {
        this.discovering = false
        return { code: 1021, msg: "停止设备扫描" }
      })
    }
    let msg = "未开启设备扫描，停止无效"
    console.log(msg)
    return Promise.resolve({ code: 1018, msg })
  }

  closeConnect() {
    //if (this.device != null && this.connecting) {
    if (this.device != null) {
      return native.closeConnect(this.device.deviceId).then(() => {
        this.connected = false
        console.log("断开已连接的设备")
        return { code: 1007, msg: "连接已断开" }
      }).catch(err => {
        let { errCode, errMsg } = err
        console.error("断开已连接的设备失败", err)
        this.onError(err)
        return { code: errCode, msg: errMsg }
      })
    } else {
      let msg = "设备未连接，断开无效"
      console.log(msg)
      return Promise.resolve({ code: 1017, msg })
    }
  }

  //监听到蓝牙连接出错
  onError(err) {
    this.listener.onError && this.listener.onError(err)
  }

  createConnect(device) {
    if (device) {
      this.device = device
    }
    if (!this.listener.auto_connect) {
      this.stopDiscovery()
    }
    return sleep(30).then(() => {
      return this.connect()
    })
  }

  connect() {
    if (this.device && this.connected) {
      let msg = "设备已连接，无需再连接"
      console.log(msg)
      return Promise.resolve({ code: 1019, msg })
    }
    if (!this.device) {
      let msg = "未扫描到设备，无法连接"
      console.log(msg)
      return Promise.resolve({ code: 1021, msg })
    }
    //this.device = device
    let { deviceId, advertisArrayData, advertisServiceUUIDs } = this.device

    let obj = {
      deviceId,
      timeout: 30000
    }

    if (this.listener.overtime) {
      obj.timeout = this.listener.overtime
    }

    this.listener.onToBeConnect && this.listener.onToBeConnect()

    console.log("开始连接设备:", logTime(), this.device)
    this.log.debug("开始连接设备:", this.device)
    this.connecting = true

    let timestamp = +new Date

    return native.createConnect(obj).then(() => {
      //this.connecting = false
      console.log("蓝牙连接设备成功,耗时:", +new Date - timestamp + "ms")
      console.log("连接设备成功:", logTime())
      this.listener.onConnectSuccess && this.listener.onConnectSuccess()

      if (isWsp(advertisArrayData, advertisServiceUUIDs)) {
        this.wspMode = true
        this.nowUUID = ''
      } else {
        this.wspMode = false
      }
      return this.deviceService(deviceId)
    }).then(res => {
      let type = this.listener.type

      if (type == 'scale') {
        res.services.forEach((service) => {
          if (service.uuid === UUID_WSP_1805 && this.wspMode) {
            this.readCharacteristicId = UUID_WSP_READ
          } else if (service.uuid === this.listener.serviceId) {
            this.readCharacteristicId = this.listener.readCharacteristicId
            this.isScale1 = false
          } else if (service.uuid === this.listener.serviceId1) {
            this.readCharacteristicId = this.listener.readCharacteristicId1
            this.isScale1 = true
          }
        })
      }
      return this.deviceCharacteristics()
    }).then(() => {
      return this.characteristicNotify()
    }).then(() => {
      return this.wspConnectDispose()
    }).then(() => {
      this.listener.connectSuccess && this.listener.connectSuccess()
      this.log.debug('连接成功')
      return Promise.resolve({ code: 1008, msg: "连接成功" })
    }).catch(err => {
      let { errCode, errMsg } = err

      this.connecting = false
      switch (errCode) {
        case 10003:
        case 10006:
        case 10012:
          this.device = null
          break;
      }
      if (errCode == 10003) {
        console.log("蓝牙连接设备失败,耗时:", +new Date - timestamp + "ms")
        console.error("连接蓝牙出错:", logTime())
        this.log.debug("连接蓝牙出错:", err)
      }
      this.onError(err)
      return Promise.resolve({ code: errCode, msg: errMsg })
    })
  }

  deviceService() {
    return native.deviceService(this.device.deviceId)
  }

  deviceCharacteristics() {
    let serviceId = ""
    if (this.wspMode) {
      serviceId = UUID_WSP_1805
    } else if (this.isScale1) {
      serviceId = this.listener.serviceId1
    } else {
      serviceId = this.listener.serviceId
    }
    return native.deviceCharacteristics(this.device.deviceId, serviceId)
  }

  characteristicNotify() {
    if (Array.isArray(this.readCharacteristicId)) {
      this.readCharacteristicId.forEach(item => {
        console.log("准备发现特征值 main", item)

        this.notifyBLECharacteristicValueChange(item)
      })
    } else {
      console.log("准备发现特征值 main", this.readCharacteristicId)
      this.notifyBLECharacteristicValueChange(this.readCharacteristicId)
    }
  }

  notifyBLECharacteristicValueChange(characteristicId) {
    let serviceId = ""
    if (this.wspMode) {
      serviceId = UUID_WSP_1805
    } else if (this.isScale1) {
      serviceId = this.listener.serviceId1
    } else {
      serviceId = this.listener.serviceId
    }
    return native.notifyBLECharacteristicValueChange(this.device.deviceId, serviceId, characteristicId)
  }

  connectedBluetoothDevices() {
    let bool = false
    return native.connectedBluetoothDevices([this.listener.serviceId]).then(res => {
      if (res.devices.length) {
        this.device = res.devices[0]
        bool = true
      }
      return Promise.resolve(bool)
    })
  }

  //监听连接状态变化
  onConnectStateChange({ deviceId, connected }) {
    this.connected = connected
    this.log.debug('连接状态', connected)
    if (!connected) {
      console.log("连接断开时间", logTime())
          //this.closeConnect()
          //this.device = null
      this.connecting = false
    }

    this.listener.onConnectStateChange(deviceId, connected)

  }

  //监听蓝牙设备状态变化
  onAdapterStateChange({ available, discovering }) {
    this.available = available
    this.discovering = discovering

    if (!available) {
      this.device = null
      this.connected = false
      this.oneToOne = false
      this.wspMode = false
    }

    this.listener.onAdapterStateChange && this.listener.onAdapterStateChange(available, discovering)
  }

  wspConnectDispose() {
    if (this.wspMode) {

      return this.writeTime()
        .then(() => {
          return this.wspDeviceServices(UUID_WSP_181B, UUID_WSP_FFE1)
        })
        .then(() => {
          return this.writeUnit()
        })
        .then(() => {
          return this.writeUser()
        })
        .then(() => {
          return this.readMeasureWeight()
        })
        .then(() => {
          return this.readIndexMeasure()
        })
    }
    return Promise.resolve()
  }

  writeTime() {
    this.nowUUID = ''
    return this.writeData(wspTime(), UUID_WSP_1805, UUID_WSP_2A2B)
  }

  writeUnit() {
    const sendCmd = this.wspBuildCmd(0x03, 0, 1)
    return this.writeData(sendCmd, UUID_WSP_181B, UUID_WSP_FFE2)
  }

  readMeasureWeight() {
    this.nowUUID = UUID_WSP_2A9D
    return this.wspDeviceServices(UUID_WSP_181D, UUID_WSP_2A9D)
  }

  readIndexMeasure() {
    return this.wspDeviceServices(UUID_WSP_181B, UUID_WSP_2A9C)
  }

  writeUser() {
    let { gender, height, birthday, userAge, person_body_shape, person_goal } = this.listener

    return this.wspDeviceServices(UUID_WSP_181C, UUID_WSP_2A9F)
      .then(() => {
        return this.writeData([0x02, 0xaa, 0x0f, 0x27], UUID_WSP_181C, UUID_WSP_2A9F)
      })
      .then(res => {
        return this.writeGender(+!(+gender))
      })
      .then(() => {
        return this.writeHeight(+height)
      })
      .then(() => {
        return this.writeBirthday(birthday)
      })
      .then(() => {
        return this.writeAge(+userAge)
      })
      .then(() => {
        return this.writeAlgorithm(algorithmValue(person_body_shape, person_goal))
      })
  }

  writeGender(v) {
    return this.writeData([v], UUID_WSP_181C, UUID_WSP_2A8C)
  }

  writeHeight(v) {
    return this.writeData([v & 0xff, v >> 8], UUID_WSP_181C, UUID_WSP_2A8E)
  }

  writeBirthday(dirthday) {
    let d = dirthday.split('-')
    return this.writeData([+d[0] & 0xff, +d[0] >> 8, +d[1], +d[2]], UUID_WSP_181C, UUID_WSP_2A85)
  }

  writeAge(v) {
    return this.writeData([v], UUID_WSP_181C, UUID_WSP_2A80)
  }

  writeAlgorithm(v) {
    return this.writeData([v, 0], UUID_WSP_181C, UUID_WSP_2AFF)
  }

  wspDeviceServices(serviceId, characteristicId) {
    if (this.wspMode) {
      let { deviceId } = this.device

      return promise(wx.getBLEDeviceServices)({
        deviceId
      }).then(res => {
        // console.log('getBLEDeviceServices', res)
        return promise(wx.getBLEDeviceCharacteristics)({
          deviceId: deviceId,
          serviceId,
        })
      }).then(res => {
        // console.log('getBLEDeviceCharacteristics', res)
        return promise(wx.notifyBLECharacteristicValueChange)({
          deviceId,
          serviceId,
          characteristicId,
          state: true,
        })
      })
    }
    return Promise.resolve()
  }

  buildCmd(cmd, deviceType, ...data) {
    const cmdData = [cmd, data.length + 4, deviceType]
    let checksum = 0
    cmdData.push(...data)
    cmdData.forEach((item) => checksum += item)
    checksum = checksum & 0xFF
    cmdData.push(checksum)

    let str = "写入数据: " + arrayToHexString(cmdData)

    console.log(str)
    this.log.debug(str)
    return cmdData

  }

  wspBuildCmd(...cmd) {
    let cmdData = [...cmd]
    let checksum = 0;
    cmdData.forEach((item) => checksum += item);
    checksum = checksum & 0xFF;
    cmdData.push(checksum);

    let str = "写入数据: " + arrayToHexString(cmdData)

    console.log(str);
    this.log.debug(str)
    return cmdData;

  }

  writeData(cmd, $serviceId, $uuid) {
    if (!this.device) return

    const ab = new ArrayBuffer(cmd.length)
    const dv = new DataView(ab)
    cmd.forEach((value, index) => {
      dv.setUint8(index, value)
    })
    let serviceId, characteristicId

    if (this.isScale1) {
      serviceId = this.listener.serviceId1
      characteristicId = this.listener.writeCharacteristicId1
    } else {
      serviceId = this.listener.serviceId
      characteristicId = this.listener.writeCharacteristicId
    }
    if ($serviceId) {
      serviceId = $serviceId
    }
    if ($uuid) {
      characteristicId = $uuid
    }
    const cmdString = arrayToHexString(cmd)
    this.log.debug('写入的值', cmdString)
    $log.writeBody('写入的值', cmdString)
    //console.log("写入的characteristicId", serviceId, characteristicId)
    return promise(wx.writeBLECharacteristicValue)({
      deviceId: this.device.deviceId,
      serviceId,
      characteristicId,
      value: ab,
    })
  }

  //监听扫描设备列表
  onDeviceScan(deviceObj) {
    const device = deviceObj.devices[0]

    console.log("扫描到的设备",deviceObj.devices.length)
    this.log.debug("扫描到的设备", device)
    if (device.RSSI === 127 || device.RSSI < -90) {
      return
    }
    const { advertisData, advertisServiceUUIDs } = device

    if (advertisData == null) return

    const advertisArrayData = new Uint8Array(advertisData)
    const advertisDataString = arrayToHexString(advertisArrayData)

    if (!advertisArrayData || advertisDataString.length === 0) {
      return
    }
    /* if (advertisArrayData[2] != 0xaa && advertisArrayData[3] != 0xbb) {
      return
    } */
    if (this.listener.name.includes(device.localName)) {
      const hasScreenOn = advertisArrayData[5] !== 1

      // if (advertisServiceUUIDs[0] != UUID_WSP_181D) { //wsp 没有开机判断
      if (!isWsp(advertisArrayData, advertisServiceUUIDs)) { //wsp 没有开机判断
        if (!hasScreenOn) {
          //过滤没有开机的设备
          return
        }
      }
    } else {
      if (advertisArrayData[2] == 0xaa && advertisArrayData[3] == 0xbb) {
        let name = 'QS1'
        device.localName = name
        device.name = name
        this.oneToOne = true
      } else {
        this.oneToOne = false
        return
      }
    }

      /*  if (this.device) return
  this.device = device
*/
      /* let mac = this.macObj[this.listener.type](advertisArrayData)

      //if (mac != "A4:C1:38:CA:CE:C2" && mac != "E1:CF:31:5D:B6:93") return

      let deviceInfo = {}

      deviceInfo.name = device.name
      deviceInfo.mac = mac
      deviceInfo.manufacturer = advertisDataString
      deviceInfo.internal_model = advertisDataString.substring(4, 8)
      deviceInfo.type = 1
      deviceInfo.device = device
      deviceInfo.rssi = device.RSSI */

      //this.listener.onDeviceScan && this.listener.onDeviceScan(deviceInfo)
    this.onDeviceScanObj[this.listener.type](device)
  }

}

export default Main